#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemBlocksScanner.h"

#include "Subsystems/BlockBehaviors/SubsystemBlockBehaviors.h"
#include "Subsystems/BlockBehaviors/SubsystemPollableBlockBehavior.hpp"

#include "Utils/Utils.h"
#include "Log.h"
#include "GameSingleton.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    static const std::string subsystemName = "BlocksScanner";
    const std::string &SubsystemBlocksScanner::getName() const
    {
        return subsystemName;
    }

    // 一帧最多处理一个区块
    void SubsystemBlocksScanner::update(float dt)
    {
        // if(m_subsystemTimeOfDay->periodicGameTimeEvent(0.5, 0))
        // {
        // }

        if (m_waitHandleChunkStack.empty())
        {
            std::vector<Point2> terrainCoordsVec;
            m_subsystemTerrain->getAllTerrainChunkCoords(terrainCoordsVec);
            for (auto it = terrainCoordsVec.begin(); it != terrainCoordsVec.end(); ++it)
            {
                m_waitHandleChunkStack.push(*it);
            }
        }
        else
        {
            Point2 coords = m_waitHandleChunkStack.top();
            m_waitHandleChunkStack.pop();

            TerrainChunk *chunk = m_subsystemTerrain->getTerrainChunk(coords.posX, coords.posY);
            if (chunk != nullptr)
            {
                for (int32_t thePosX = 0; thePosX < 16; thePosX++)
                {
                    for (int32_t thePosZ = 0; thePosZ < 16; thePosZ++)
                    {
                        int32_t index = TerrainChunk::CalculateCellIndex(thePosX, 0, thePosZ);
                        for (int32_t thePosY = 0; thePosY < 256; thePosY++)
                        {
                            int32_t cellValueFast = chunk->getCellValueFast(index + thePosY);
                            int32_t cellContent = Terrain::ExtractContents(cellValueFast);
                            if (cellContent != 0)
                            {
                                for (SubsystemPollableBlockBehavior *behaviorPtr : m_pollableBehaviorsByContents[cellContent])
                                {
                                    behaviorPtr->onPoll(cellValueFast, chunk->origin.posX + thePosX, thePosY, chunk->origin.posY + thePosZ);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    void SubsystemBlocksScanner::load(const nlohmann::json &json)
    {
        m_subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        m_subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        auto *subsystemBlockBehaviors = GameSingleton::singletonPtr->getSingleton<SubsystemBlockBehaviors>();
        auto *blockBehaviorsByContents = subsystemBlockBehaviors->getBlockBehaviorsByContents();

        for (int32_t index = 0; index < 1024; ++index)
        {
            for (auto blockBehaviorPtr : blockBehaviorsByContents[index])
            {
                SubsystemPollableBlockBehavior *pollableBlockBehaviorPtr = dynamic_cast<SubsystemPollableBlockBehavior *>(blockBehaviorPtr);
                if (pollableBlockBehaviorPtr != nullptr)
                {
                    m_pollableBehaviorsByContents[index].push_back(pollableBlockBehaviorPtr);
                }
            }
        }
    }

    void SubsystemBlocksScanner::save(nlohmann::json &json) {}
    void SubsystemBlocksScanner::saveToMsgJson(nlohmann::json &jsonData) {}

    SubLoadEchelon SubsystemBlocksScanner::getLoadEchelon()
    {
        return SubLoadEchelon::Second;
    }
    SubUpdateType SubsystemBlocksScanner::getUpdateType()
    {
        return SubUpdateType::MultiThreadUpdate;
    }
}